﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SlimDX;
using SlimDX.Direct3D10;

using System.Drawing;

namespace SGX
{
    class EffectState
    {
        Effect effect;
        Dictionary<string, BlendStateSet> blendStates;
        Dictionary<string, DepthStencilStateSet> depthStencilStates;
        Dictionary<string, RasterizerStateSet> rasterizerSates;
        Dictionary<string, EffectVariable> shaderVariables;

        public EffectState()
        {
            blendStates = new Dictionary<string, BlendStateSet>();
            depthStencilStates = new Dictionary<string, DepthStencilStateSet>();
            rasterizerSates = new Dictionary<string, RasterizerStateSet>();
            shaderVariables = new Dictionary<string, EffectVariable>();
        }

        public Result LoadEffect(string srcFile, string parentSrcFile)
        {
            string compilationError;
            EffectPool pool = null;
            if (parentSrcFile != null)
            {
                pool = GlobalResourceCache.CreateEffectPoolFromFile(parentSrcFile, out compilationError);
                if (pool == null)
                    throw new ArgumentException("effect pool could not be created from file : " + parentSrcFile);
            }

            if (parentSrcFile == null)
                effect = GlobalResourceCache.CreateEffectFromFile(srcFile, out compilationError);
            else
                effect = GlobalResourceCache.CreateChildEffectFromFile(srcFile, pool, out compilationError);
            return (effect == null ? ResultCode.Failure : ResultCode.Success);
        }
        public Result LoadEffectPool(string srcFile)
        {
            string compilationError;
            effect = GlobalResourceCache.CreateEffectPoolFromFile(srcFile, out compilationError).AsEffect();
            return (effect == null ? ResultCode.Failure : ResultCode.Success);
        }
        public Effect Effect { get { return effect; } }
        public BlendStateSet GetorCreateBlendState(string name)
        {
            BlendStateSet state;
            bool result = blendStates.TryGetValue(name, out state);
            if (result == true)
                return state;
            else
            {
                state = new BlendStateSet(this);
                blendStates.Add(name, state);
                return state;
            }
        }
        public DepthStencilStateSet GetorCreateDpethStencilState(string name)
        {
            DepthStencilStateSet state;
            bool result = depthStencilStates.TryGetValue(name, out state);
            if (result == true)
                return state;
            else
            {
                state = new DepthStencilStateSet(this);
                depthStencilStates.Add(name, state);
                return state;
            }
        }
        public RasterizerStateSet GetorCreateRasterizerState(string name)
        {
            RasterizerStateSet state;
            bool result = rasterizerSates.TryGetValue(name, out state);
            if (result == true)
                return state;
            else
            {
                state = new RasterizerStateSet(this);
                rasterizerSates.Add(name, state);
                return state;
            }
        }
        public Result AddShaderVariable(ShaderVariableType type, string name)
        {
            EffectVariable var = effect.GetVariableByName(name);
            if(var != null)
            {
                ShaderVariableType t;
                if (var is EffectConstantBuffer)
                    t = ShaderVariableType.ConstantBuffer;
                else if (var is EffectMatrixVariable)
                    t = ShaderVariableType.Matrix;
                else if (var is EffectResourceVariable)
                    t = ShaderVariableType.Resource;
                else if (var is EffectScalarVariable)
                    t = ShaderVariableType.Scalar;
                else if (var is EffectStringVariable)
                    t = ShaderVariableType.String;
                else if (var is EffectVectorVariable)
                    t = ShaderVariableType.Vector;
                else
                    return ResultCode.Failure;
                if (t != type)
                    return ResultCode.Failure;

                shaderVariables.Add(name, var);
                return ResultCode.Success;
            }
            return ResultCode.Failure;
        }

        public void ApplyBlendState(string name)
        {
            GlobalResourceCache.Device.OutputMerger.BlendState = blendStates[name].State;
        }
        public void ApplyDepthStencilState(string name)
        {
            GlobalResourceCache.Device.OutputMerger.DepthStencilState = depthStencilStates[name].State;
        }
        public void ApplyRasterizerState(string name)
        {
            GlobalResourceCache.Device.Rasterizer.State = rasterizerSates[name].State;
        }

        //Update values of shader variables
        public Result SetRawValue(string name, DataStream data, int count)
        {
            return shaderVariables[name].SetRawValue(data, count);
        }
        public Result SetConstantBuffer(string name, SlimDX.Direct3D10.Buffer buffer)
        {
            return (shaderVariables[name].AsConstantBuffer()).SetConstantBuffer(buffer);
        }
        public Result SetTextureBuffer(string name, ShaderResourceView buffer)
        {
            return (shaderVariables[name].AsConstantBuffer()).SetTextureBuffer(buffer);
        }
        public Result SetMatrix(string name, Matrix[] matrices)
        {
            return (shaderVariables[name].AsMatrix()).SetMatrix(matrices);
        }
        public Result SetMatrix(string name, Matrix matrix)
        {
            return (shaderVariables[name].AsMatrix()).SetMatrix(matrix);
        }
        public Result SetResource(string name, ShaderResourceView view)
        {
            return (shaderVariables[name].AsResource()).SetResource(view);
        }
        public Result Set(string name, int[] values, bool vector)
        {
            if (vector == true)
            {
                var res = (shaderVariables[name].AsVector()).Set(values);
                return res;
            }
            else
                return (shaderVariables[name].AsScalar()).Set(values);
        }
        public Result Set(string name, float[] values)
        {
            return (shaderVariables[name].AsScalar()).Set(values);
        }
        public Result Set(string name, bool[] values, bool vector)
        {
            if(vector)
                return (shaderVariables[name].AsVector()).Set(values);
            else
                return (shaderVariables[name].AsScalar()).Set(values);
        }
        public Result Set(string name, int value)
        {
            return (shaderVariables[name].AsScalar()).Set(value);
        }
        public Result Set(string name, float value)
        {
            return (shaderVariables[name].AsScalar()).Set(value);
        }
        public Result Set(string name, bool value)
        {
            return (shaderVariables[name].AsScalar()).Set(value);
        }
        public Result Set(string name, Color4[] values)
        {
            return (shaderVariables[name].AsVector()).Set(values);
        }
        public Result Set(string name, Vector4[] values)
        {
            return (shaderVariables[name].AsVector()).Set(values);
        }
        public Result Set(string name, Color4 value)
        {
            return (shaderVariables[name].AsVector()).Set(value);
        }
        public Result Set(string name, Vector4 value)
        {
            return (shaderVariables[name].AsVector()).Set(value);
        }
        public Result Set(string name, Vector3 value)
        {
            return (shaderVariables[name].AsVector()).Set(value);
        }
        public Result Set(string name, Vector2 value)
        {
            return (shaderVariables[name].AsVector()).Set(value);
        }
    }

    class BlendStateSet
    {
        EffectState effectState;
        BlendStateDescription desc;
        BlendState state;

        public BlendStateSet(EffectState state)
        {
            effectState = state;

            desc.IsAlphaToCoverageEnabled = false;
            desc.SetBlendEnable(0, true);
            desc.SetWriteMask(0, ColorWriteMaskFlags.All);
            desc.SourceBlend = BlendOption.SourceAlpha;
            desc.DestinationBlend = BlendOption.InverseSourceAlpha;
            desc.BlendOperation = BlendOperation.Add;
            desc.SourceAlphaBlend = BlendOption.One;
            desc.DestinationAlphaBlend = BlendOption.Zero;
            desc.AlphaBlendOperation = BlendOperation.Add;

            this.state = null;
        }
        public BlendState State { get { return state; } }

        public bool IsAlphaToCoverageEnabled
        {
            get { return desc.IsAlphaToCoverageEnabled; }
            set { desc.IsAlphaToCoverageEnabled = value; }
        }

        public void SetBlendEnable(uint index, bool value)
        {
            desc.SetBlendEnable(index, value);
        }

        public void SetWriteMask(uint index, ColorWriteMaskFlags value)
        {
            desc.SetWriteMask(index, value);
        }

        public BlendOption SourceBlend { get { return desc.SourceBlend; } set { desc.SourceBlend = value; } }
        public BlendOption DestinationBlend { get { return desc.DestinationBlend; } set { desc.DestinationBlend = value; } }
        public BlendOption SourceAlphaBlend { get { return desc.SourceAlphaBlend; } set { desc.SourceAlphaBlend = value; } }
        public BlendOption DestinationAlphaBlend { get { return desc.DestinationAlphaBlend; } set { desc.DestinationAlphaBlend = value; } }
        public BlendOperation BlendOperation { get { return desc.BlendOperation; } set { desc.BlendOperation = value; } }
        public BlendOperation AlphaBlendOperation { get { return desc.AlphaBlendOperation; } set { desc.AlphaBlendOperation = value; } }

        public void Commit()
        {
            state = GlobalResourceCache.CreateBlendState(desc);
        }
    }

    class DepthStencilStateSet
    {
        EffectState effectState;
        DepthStencilStateDescription desc;
        DepthStencilState state;

        public DepthStencilStateSet(EffectState state)
        {
            effectState = state;

            desc.IsDepthEnabled = true;
            desc.IsStencilEnabled = false;
            desc.StencilReadMask = 0xff;
            desc.StencilWriteMask = 0xff;
            desc.DepthWriteMask = DepthWriteMask.All;
            desc.DepthComparison = Comparison.Less;

            this.state = null;
        }
        public DepthStencilState State { get { return state; } }

        public bool IsDepthEnabled { get { return desc.IsDepthEnabled; } set { desc.IsDepthEnabled = value; } }
        public bool IsStencilEnabled { get { return desc.IsStencilEnabled; } set { desc.IsStencilEnabled = value; } }
        public byte StencilReadMask { get { return desc.StencilReadMask; } set { desc.StencilReadMask = value; } }
        public byte StencilWriteMask { get { return desc.StencilWriteMask; } set { desc.StencilWriteMask = value; } }
        public DepthWriteMask DepthWriteMask { get { return desc.DepthWriteMask; } set { desc.DepthWriteMask = value; } }
        public Comparison DepthComparison { get { return desc.DepthComparison; } set { desc.DepthComparison = value; } }
        public DepthStencilOperationDescription BackFace { get { return desc.BackFace; } set { desc.BackFace = value; } }
        public DepthStencilOperationDescription FrontFace { get { return desc.FrontFace; } set { desc.FrontFace = value; } }

        public void Commit()
        {
            state = GlobalResourceCache.CreateDepthStencilState(desc);
        }
    }

    class RasterizerStateSet
    {
        RasterizerStateDescription desc;
        EffectState effectState;
        RasterizerState state;

        public RasterizerStateSet(EffectState state)
        {
            effectState = state;

            desc.FillMode = FillMode.Solid;
            desc.CullMode = CullMode.Back;
            desc.IsFrontCounterclockwise = true;
            desc.DepthBias = 0;
            desc.DepthBiasClamp = 0;
            desc.SlopeScaledDepthBias = 0;
            desc.IsAntialiasedLineEnabled = false;
            desc.IsDepthClipEnabled = false;
            desc.IsMultisampleEnabled = false;
            desc.IsScissorEnabled = false;

            this.state = null;
        }
        public RasterizerState State { get { return state; } }

        public FillMode FillMode { get { return desc.FillMode; } set { desc.FillMode = value; } }
        public CullMode CullMode { get { return desc.CullMode; } set { desc.CullMode = value; } }
        public bool IsFrontCounterClockwise { get { return desc.IsFrontCounterclockwise; } set { desc.IsFrontCounterclockwise = value; } }
        public int DepthBias { get { return desc.DepthBias; } set { desc.DepthBias = value; } }
        public float DepthBiasClamp { get { return desc.DepthBiasClamp; } set { desc.DepthBiasClamp = value; } }
        public float SlopeScaledDepthBias { get { return desc.SlopeScaledDepthBias; } set { desc.SlopeScaledDepthBias = value; } }
        public bool IsAntialiasedLineEnabled { get { return desc.IsAntialiasedLineEnabled; } set { desc.IsAntialiasedLineEnabled = value; } }
        public bool IsDepthClipEnabled { get { return desc.IsDepthClipEnabled; } set { desc.IsDepthClipEnabled = value; } }
        public bool IsMultisampleEnabled { get { return desc.IsMultisampleEnabled; } set { desc.IsMultisampleEnabled = value; } }
        public bool IsScissorEnabled { get { return desc.IsScissorEnabled; } set { desc.IsScissorEnabled = value; } }

        public void Commit()
        {
            state = GlobalResourceCache.CreateRasterizerState(desc);
        }
    }
}